3D Touch 开发 入门

图标快捷菜单

iOS 9 新增了桌面图标快捷菜单的功能(Home Screen Quick Actions)。用户按压桌面图标能弹出几个选项,进行某些快捷操作。

iOS 9 下每个应用最多能显示4个桌面快捷选项。系统优先显示静态快捷选项,从上往下。如果未超出限制,且应用有定义动态快捷选项,显示动态快捷选项。

静态快捷选项

在应用的 Info.plist 顶层中新增 UIApplicationShortcutItems 数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<key>UIApplicationShortcutItems</key>
<array>
<dict>
<key>UIApplicationShortcutItemIconType</key>
<string>UIApplicationShortcutIconTypeSearch</string>
<key>UIApplicationShortcutItemSubtitle</key>
<string>shortcutSubtitle1</string>
<key>UIApplicationShortcutItemTitle</key>
<string>shortcutTitle1</string>
<key>UIApplicationShortcutItemType</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).First</string>
<key>UIApplicationShortcutItemUserInfo</key>
<dict>
<key>firstShorcutKey1</key>
<string>firstShortcutKeyValue1</string>
</dict>
</dict>
<dict>
<key>UIApplicationShortcutItemIconType</key>
<string>UIApplicationShortcutIconTypeShare</string>
<key>UIApplicationShortcutItemSubtitle</key>
<string>shortcutSubtitle2</string>
<key>UIApplicationShortcutItemTitle</key>
<string>shortcutTitle2</string>
<key>UIApplicationShortcutItemType</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).Second</string>
<key>UIApplicationShortcutItemUserInfo</key>
<dict>
<key>secondShortcutKey1</key>
<string>secondShortcutValue1</string>
</dict>
</dict>
</array>

静态定义快速在运行时常用的key:
key | 说明
— | —
UIApplicationShortcutItemType | (必须使用) 用来区分与其他快速选项的分类
UIApplicationShortcutItemTitle | (必须使用) 快速选项显示的标题
UIApplicationShortcutItemSubtitle | 快速选项显示的子标题
UIApplicationShortcutItemIconType | 图片类型由系统提供
UIApplicationShortcutItemIconFile | 自定义的图标
UIApplicationShortcutItemUserInfo | 附加信息

动态快捷选项

指定[UIApplication sharedApplication].shortcutItems。
动态快捷选项必须启动应用且设置shortcutItems后,才能生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- (void)setupApplicationShortcutItems
{
UIApplicationShortcutIcon *homeIcon =
[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeCompose];
NSDictionary *userInfo1 = @{@"key":@"home"};
NSDictionary *userInfo2 = @{@"key":@"message"};
UIMutableApplicationShortcutItem *homeShortcutItem =
[[UIMutableApplicationShortcutItem alloc] initWithType:@"home"
localizedTitle:@"主页"
localizedSubtitle:@"跳转主页"
icon:homeIcon
userInfo:userInfo1];
UIMutableApplicationShortcutItem *messageShortcutItem =
[[UIMutableApplicationShortcutItem alloc] initWithType:@"message"
localizedTitle:@"消息"
localizedSubtitle:@"跳转消息"
icon:nil
userInfo:userInfo2];
NSArray *items = @[homeShortcutItem,messageShortcutItem];
[UIApplication sharedApplication].shortcutItems = items;
}

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self setupApplicationShortcutItems];
...
}

快捷选项操作

在AppDelegate.m文件中添加
application:performActionForShortcutItem:completionHandler:
代理方法,根据UIApplicationShortcutItem的type属性和之前在info.plist设置UIApplicationShortcutItemType对应的在值来判断,用户点击的是哪个快速选项。这个方法在接近激活应用时就会调用,除了
-application:willFinishLaunchingWithOptions:
or
-application:didFinishLaunchingWithOptions
returns NO.
这种情况。

在-application:didFinishLaunchingWithOptions
中可以通过UIApplicationLaunchOptionsShortcutItemKey键来获取当前接收快速选项的UIApplicationShortcutItem对象,来处理用户的意图。

Peek 和 Pop

这个功能是一套全新的用户交互机制,在使用3D Touch时,ViewController中会有如下三个交互阶段:

  1. 提示用户这里有3D Touch的交互,会使交互控件周围模糊
  2. 继续深按,会出现预览视图
  3. 通过视图上的交互控件进行进一步交互

peek 和 pop 的核心主要是 iOS9 上新增的以下几个代理方法:

  • registerForPreviewingWithDelegate:sourceView:
  • previewingContext:viewControllerForLocation:
  • previewingContext:commitViewController:
  • previewActionItems

下面结合几段代码看看 peek 和 pop 的用法。

假设现在有两个 ViewController,MasterViewController 和 DetailViewController,MasterViewController 上有一个 TableView,点击上面的 Cell 进入 DetailViewController。

首先,MasterViewController 需要调用注册接口以支持3D touch,指定接收3D touch 事件的 view 和处理 peek 和 pop 的代理 。

1
2
3
4
5
6
// 判断设备是否支持3D touch
if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
// 支持 3D touch 的话,注册 ViewController 以使用 peek 和 pop。
// 指定 Previewing 的代理为 MasterViewcontroller 自己。
[self registerForPreviewingWithDelegate:self sourceView:self.view];
}

如果考虑得周全一点,应该在识别到用户关闭了 3D touch 时,调用unregisterForPreviewingWithContext:注销 3D touch。

接下来,在 MasterViewController 上需要实现以下代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Pop 事件代理, 重按时进入 DetailViewController
- (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext
commitViewController:(UIViewController *)viewControllerToCommit {
[self showViewController:viewControllerToCommit sender:self];
}

// Peek 事件代理,识别 Peek 操作点击区域,指定预览的 ViewController
- (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext
viewControllerForLocation:(CGPoint)location {
NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath];
if (cell == nil) return nil;

DetailViewController *detailViewController = [DetailViewController new];
// 读取对应 model 数据到 DetailViewController 中。
[detailViewControler loadDataFromModel:self.modelList[indexPath.row]];

// 设置点击的 cell,聚集此处,周围会产生模糊效果
[previewingContext setSourceRect:cell.frame];
// 设置预览框大小,设置或设置为0会使用默认大小。
[detailViewController setPreferredContentSize = CGSizeMake(0, previewDetail.preferredHeight)];

return cv;
}

通常,在弹出预览窗口后,可以上滑,弹出菜单,进行某些快捷操作。前提是预览的 ViewController 实现了-previewActionItems,指定了peek 快捷操作。如果没实现该代理,则没有快捷菜单,弹出预览后滑动没有效果,放开就消失了。
快捷菜单可以通过 UIPreiveActionGroup 组织成多个层级,详情见代码。

以下是 DetailViewController 的 previewActionItems 示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"action1"
style:UIPreviewActionStyleDefault
handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"preview action 1");
}];
UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"action2"
style:UIPreviewActionStyleDestructive
handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"preview action 2");
}];
UIPreviewAction *action3 = [UIPreviewAction actionWithTitle:@"action3"
style:UIPreviewActionStyleSelected
handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"preview action 3");
}];
UIPreviewActionGroup *actionGroup = [UIPreviewActionGroup actionGroupWithTitle:@"action group"
style:UIPreviewActionStyleDefault
actions:@[action2, action3] ];
return @[action1, actionGroup];
}

UITouch

UITouch 新增两个用于 3d touch 的字段: force 和 maximumPossibleForce。这些属性可以让你侦测并响应APP接收的UIEvent触摸压力。